/*
 * Copyright 2012-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.origin;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * 接口，用于唯一标识一个元素的来源。例如，从 {@link File} 加载的元素，其来源可能包含文件名以及行号/列号信息。
 * <p>
 * 实现类必须提供合理的 {@code hashCode()}、{@code equals(...)} 和 {@code #toString()} 实现。
 *
 * @author Madhura Bhave
 * @author Phillip Webb
 * @since 2.0.0
 * @see OriginProvider
 * @see TextResourceOrigin
 */
public interface Origin {

	/**
	 * 返回该实例的父来源（如果有）。父来源提供创建此元素的元素的来源信息。
	 * @return 父来源或 {@code null}
	 * @since 2.4.0
	 * @see Origin#parentsFrom(Object)
	 */
	default Origin getParent() {
		return null;
	}

	/**
	 * 查找对象的 {@link Origin} 来源。检查源对象是否为 {@link Origin} 或 {@link OriginProvider}，
	 * 也会搜索异常堆栈。
	 * @param source 源对象或 {@code null}
	 * @return 一个可选的 {@link Origin}
	 */
	static Origin from(Object source) {
		if (source instanceof Origin) {
			return (Origin) source;
		}
		Origin origin = null;
		if (source instanceof OriginProvider) {
			origin = ((OriginProvider) source).getOrigin();
		}
		if (origin == null && source instanceof Throwable) {
			return from(((Throwable) source).getCause());
		}
		return origin;
	}

	/**
	 * 查找对象来源的所有父来源。检查源对象是否为 {@link Origin} 或 {@link OriginProvider}，
	 * 也会搜索异常堆栈。返回从最直接父来源开始，直到根 {@link Origin} 的所有父来源列表。
	 * @param source 源对象或 {@code null}
	 * @return 父来源列表，若源对象为 {@code null}、无来源或无父来源，则返回空列表
	 * @since 2.4.0
	 */
	static List<Origin> parentsFrom(Object source) {
		Origin origin = from(source);
		if (origin == null) {
			return Collections.emptyList();
		}
		Set<Origin> parents = new LinkedHashSet<>();
		origin = origin.getParent();
		while (origin != null && !parents.contains(origin)) {
			parents.add(origin);
			origin = origin.getParent();
		}
		return Collections.unmodifiableList(new ArrayList<>(parents));
	}

}
